package com.dragon.common.utils.time;

import com.dragon.common.utils.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;

/**
 * 日期时间工具类
 *
 * @author dragon
 * @version 1.0
 * @date 2024/7/17 16:01
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    private static final Log log = LogFactory.getLog(DateUtils.class);


    /**
     * 获取当前日期时间
     *
     * @return 当前日期时间
     */
    public static Date datetime() {
        return new Date();
    }

    /**
     * 获取当前日期和时间字符串
     *
     * @return 当前日期时间字符串，例如：2024-06-25 10:45:23
     */
    public static String getDatetimeStr() {
        return formatDate(datetime(), DateTimePattern.DATETIME_PATTERN);
    }

    /**
     * 获取当前日期字符串
     *
     * @return 当前日期时间字符串，例如：2024-06-25
     */
    public static String getDateStr() {
        return formatDate(datetime(), DateTimePattern.DATE_PATTERN);
    }

    /**
     * 获取当前时间字符串
     *
     * @return 时间字符串，例如 09:51:53
     */
    public static String getTimeStr() {
        return formatDate(datetime(), DateTimePattern.TIME_PATTERN);
    }

    /**
     * 获取当前星期字符串.
     *
     * @return String 当前星期字符串，例如 周二
     */
    public static String getDayOfWeekStr() {
        return formatDate(datetime(), "E");
    }

    /**
     * 获取指定日期是星期几
     *
     * @param date 日期
     * @return String 星期几
     */
    public static String getDayOfWeekStr(Date date) {
        String[] weekOfDays = {
            DateTimePattern.SUNDAY,
            DateTimePattern.MONDAY,
            DateTimePattern.TUESDAY,
            DateTimePattern.WEDNESDAY,
            DateTimePattern.THURSDAY,
            DateTimePattern.FRIDAY,
            DateTimePattern.SATURDAY
        };

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;

        return weekOfDays[dayOfWeek];
    }

    /**
     * 获取日期时间字符串
     *
     * @param date    需要转化的日期时间
     * @param pattern 时间格式
     * @return String 日期时间字符串，例如 2015-08-11 09:51:53
     */
    public static String format(Date date, String pattern) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        return simpleDateFormat.format(date);
    }

    /**
     * 获取日期时间字符串
     *
     * @param date 需要转化的日期时间
     * @return String 日期时间字符串，例如 2015-08-11 00:00:00
     */
    public static String format(Date date) {
        return format(date, DateTimePattern.DATETIME_PATTERN);
    }

    /**
     * 获取当天日期时间字符串
     *
     * @param pattern 时间格式
     * @return String 日期时间字符串，例如 2015-08-11 09:51:53
     */
    public static String format(String pattern) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        return simpleDateFormat.format(datetime());
    }

    /**
     * 获取日期时间字符串
     *
     * @param date    需要转化的日期时间
     * @param pattern 时间格式
     * @return String 日期时间字符串，例如 2015-08-11 09:51:53
     */
    public static String formatDate(Date date, String pattern) {
        return DateFormatUtils.format(date, pattern);
    }

    /**
     * 获取日期时间字符串
     *
     * @param date 需要转化的日期时间
     * @return String 日期时间字符串，例如 2015-08-11 00:00:00
     */
    public static String formatDate(Date date) {
        return formatDate(date, DateTimePattern.DATETIME_PATTERN);
    }

    /**
     * 获取当天日期时间字符串
     *
     * @param pattern 时间格式
     * @return String 日期时间字符串，例如 2015-08-11 09:51:53
     */
    public static String formatDate(String pattern) {
        return DateFormatUtils.format(datetime(), pattern);
    }

    /**
     * 日期时间字符串转化为日期时间
     *
     * @param dateStr 日期时间字符串
     * @param pattern 日期格式
     * @return 日期时间
     */
    public static Date parse(String dateStr, String pattern) {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
            return simpleDateFormat.parse(dateStr);
        } catch (ParseException parseException) {
            log.error(StringUtils.format("日期时间字符串转化为日期时间失败 {}", parseException.getMessage()), parseException);
        }

        return null;
    }

    /**
     * 日期时间字符串转化为日期时间
     *
     * @param dateStr 日期时间字符串
     * @return 日期时间
     */
    public static Date parse(String dateStr) {
        return parse(dateStr, DateTimePattern.DATETIME_PATTERN);
    }

    /**
     * 日期时间字符串转化为日期时间
     *
     * @param dateStr 日期时间字符串
     * @param pattern 日期格式
     * @return 日期时间
     */
    public static Date parseDate(String dateStr, String pattern) {
        try {
            return org.apache.commons.lang3.time.DateUtils.parseDate(dateStr, pattern);
        } catch (ParseException parseException) {
            log.error(StringUtils.format("日期时间字符串转化为日期时间失败 {}", parseException.getMessage()), parseException);
        }

        return null;
    }

    /**
     * 日期时间字符串转化为日期时间
     *
     * @param dateStr 日期时间字符串
     * @return 日期时间
     */
    public static Date parseDate(String dateStr) {
        return parseDate(dateStr, DateTimePattern.DATETIME_PATTERN);
    }

    /**
     * 获取指定日期时间加上指定天数之后的日期时间.
     *
     * @param date 日期时间
     * @param num  数量
     * @return Date 新的日期时间
     */
    public static Date nextDay(Date date, int num) {
        Calendar calendar = Calendar.getInstance();

        calendar.setTime(date);
        calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) + num);

        return calendar.getTime();
    }

    /**
     * 获取当天日期时间加上指定天数之后的日期时间.
     *
     * @param num 数量
     * @return Date 新的日期时间
     */
    public static Date nextDay(int num) {
        return nextDay(datetime(), num);
    }

    /**
     * 获取当天日期时间加上指定月数之后的日期时间.
     *
     * @param num 数量
     * @return Date 新的日期时间
     */
    public static Date nextMonth(int num) {
        return nextMonth(datetime(), num);
    }

    /**
     * 获取指定日期时间加上指定月数之后的日期时间.
     *
     * @param date 日期时间
     * @param num  数量
     * @return Date 新的日期时间
     */
    public static Date nextMonth(Date date, int num) {
        Calendar calendar = Calendar.getInstance();

        calendar.setTime(date);
        calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + num);

        return calendar.getTime();
    }

    /**
     * 获取当天日期时间加上指定年数之后的日期时间.
     *
     * @param num 数量
     * @return Date 新的日期时间
     */
    public static Date nextYear(int num) {
        return nextYear(datetime(), num);
    }

    /**
     * 获取指定日期时间加上指定年数之后的日期时间.
     *
     * @param date 日期时间
     * @param num  数量
     * @return Date 新的日期时间
     */
    public static Date nextYear(Date date, int num) {
        Calendar calendar = Calendar.getInstance();

        calendar.setTime(date);
        calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + num);

        return calendar.getTime();
    }

    /**
     * 获取本年第一天的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfYearStr() {
        return formatDate(getFirstDayOfYear(datetime()));
    }

    /**
     * 获取指定日期当年第一天的日期时间
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getFirstDayOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        // 获取指定日期当年第一天
        int firstDayOfYear = calendar.getActualMinimum(Calendar.DAY_OF_YEAR);
        calendar.set(Calendar.DAY_OF_YEAR, firstDayOfYear);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        // calendar.set(Calendar.YEAR, Calendar.JANUARY, 1, 0, 0, 0);

        return calendar.getTime();
    }

    /**
     * 获取指定日期当年最后一天的日期字符串,带日期格式化参数
     *
     * @return String 格式：yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfYearStr() {
        return formatDate(getLastDayOfYear(datetime()));
    }

    /**
     * 获取指定日期当年最后一天的日期字符串,带日期格式化参数
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getLastDayOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        // 获取指定日期当年最后一天
        int firstDayOfYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
        calendar.set(Calendar.DAY_OF_YEAR, firstDayOfYear);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);

        return calendar.getTime();
    }

    /**
     * 获取本月第一天的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfMonthStr() {
        return formatDate(getFirstDayOfMonth(datetime()));
    }

    /**
     * 获取本月最后一天的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfMonthStr() {
        return formatDate(getLastDayOfMonth(datetime()));
    }

    /**
     * 获取指定日期当月第一天的日期
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getFirstDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        // 获取指定日期当月第一天
        int firstDayOfMonth = calendar.getActualMinimum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, firstDayOfMonth);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        return calendar.getTime();
    }

    /**
     * 获取指定日期当月最后一天的日期
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getLastDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        // 获取指定日期当月第一天
        int firstDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, firstDayOfMonth);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);

        return calendar.getTime();
    }

    /**
     * 获取本周第一天的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfWeekStr() {
        return formatDate(getFirstDayOfWeek(datetime()));
    }

    /**
     * 获取本周最后一天的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfWeekStr() {
        return format(getLastDayOfWeek(datetime()));
    }

    /**
     * 获取指定日期当周第一天的日期字符串,这里第一天为周一
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getFirstDayOfWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        // 每周从周一开始
        // calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(date);

        // 获取指定日期当周第一天
        int firstDayOfWeek = calendar.getActualMinimum(Calendar.DAY_OF_WEEK);
        calendar.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
        // 默认周日为第一天，如果要以周一为第一天需要加一天
        calendar.add(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        return calendar.getTime();
    }

    /**
     * 获取指定日期当周最后一天的日期字符串,这里最后一天为周日
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getLastDayOfWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        // 每周从周一开始
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(date);

        // 获取指定日期当月第一天
        int lastDayOfWeek = calendar.getActualMaximum(Calendar.DAY_OF_WEEK);
        calendar.set(Calendar.DAY_OF_WEEK, lastDayOfWeek);
        // 默认周六为最后一天，如果要以周日为最后一天需要加一天
        calendar.add(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);

        return calendar.getTime();
    }

    /**
     * 获取今天开始时间的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 00:00:00
     */
    public static String getStartTimeOfDayStr() {
        return format(getStartTimeOfDay(datetime()));
    }

    /**
     * 获取今天结束时间的日期字符串
     *
     * @return String 格式：yyyy-MM-dd 23:59:59
     */
    public static String getEndTimeOfDayStr() {
        return format(getEndTimeOfDay(datetime()));
    }

    /**
     * 获取指定日期的开始时间
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getStartTimeOfDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        return calendar.getTime();
    }

    /**
     * 获取当天的开始时间
     *
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getStartTimeOfToday() {
        return getStartTimeOfDay(datetime());
    }

    /**
     * 获取指定日期的结束时间
     *
     * @param date 指定日期时间
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getEndTimeOfDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);

        return calendar.getTime();
    }

    /**
     * 获取当天的结束时间
     *
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getEndTimeOfToday() {
        return getEndTimeOfDay(datetime());
    }

    /**
     * 获取昨天的开始时间
     *
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getStartTimeOfYesterday() {
        return getStartTimeOfDay(nextDay(-1));
    }

    /**
     * 获取明天的结束时间
     *
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getEndTimeOfTomorrow() {
        return getEndTimeOfDay(nextDay(1));
    }

    /**
     * 获取明天的开始时间
     *
     * @return Date 格式：yyyy-MM-dd 00:00:00
     */
    public static Date getStartTimeOfTomorrow() {
        return getStartTimeOfDay(nextDay(1));
    }

    /**
     * 获取昨天的结束时间
     *
     * @return Date 格式：yyyy-MM-dd 23:59:59
     */
    public static Date getEndTimeOfYesterday() {
        return getEndTimeOfDay(nextDay(-1));
    }

    /**
     * 获取指定日期的年份
     *
     * @param date 日期
     * @return 指定日期的年份
     */
    public static int getYear(Date date) {
        String yearStr = formatDate(date, "yyyy");

        return Integer.parseInt(yearStr);
    }

    /**
     * 获取当年的年份
     *
     * @return 指定日期的年份
     */
    public static int getYear() {
        return getYear(datetime());
    }

    /**
     * 获取指定日期的月份
     *
     * @param date 日期
     * @return 指定日期的月份
     */
    public static int getMonth(Date date) {
        String monthStr = formatDate(date, "MM");

        return Integer.parseInt(monthStr);
    }

    /**
     * 获取当月的月份
     *
     * @return 指定日期的月份
     */
    public static int getMonth() {
        return getMonth(datetime());
    }

    /**
     * 获取指定日期的日份
     *
     * @param date 指定日期
     * @return 指定日期的日份
     */
    public static int getDay(Date date) {
        String dayStr = formatDate(date, "dd");

        return Integer.parseInt(dayStr);
    }

    /**
     * 获取指定日期的日份
     *
     * @return 指定日期的日份
     */
    public static int getDay() {
        return getDay(datetime());
    }

    /**
     * 计算时间差
     *
     * @param endDate   最后时间
     * @param startTime 开始时间
     * @return 时间差（天/小时/分钟）
     */
    public static String timeDistance(Date endDate, Date startTime) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - startTime.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }

    /**
     * LocalDateTime转Date
     *
     * @param temporalAccessor LocalDateTime
     * @return Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }

    /**
     * LocalDate转Date
     *
     * @param temporalAccessor LocalDate
     * @return Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        return toDate(localDateTime);
    }

}
